﻿using System;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Windows.Forms;
using System.Web.Script.Serialization;
using OAuth;

namespace Plorx
{
    class BaseAPICall
    {
        private static string api_str = "";
        protected static void call(string cat, string name)
        {
            api_str = "/APP/" + cat + "/" + name + "?";
        }
        protected enum Check { IntNoCheck, IntCheckEqualOrGreaterThanZero };
        protected static void par(string key, string value)
        {
            if (value == null)
            {
                return;
            }
            api_str += key + "=" + value + "&";
        }
        protected static void par(string key, DateTime value)
        {
            if (value == null)
            {
                return;
            }
            if (value.Year <= 1)
            {
                return;
            }
            api_str += key + "=" + datetimetostring(value) + "&";
        }
        protected static void par(string key, int value, Check check = Check.IntNoCheck)
        {
            if ((check == Check.IntCheckEqualOrGreaterThanZero) && (value < 0))
            {
                return;
            }
            api_str += key + "=" + Convert.ToString(value) + "&";
        }
        protected static void par(string key, bool value)
        {
            api_str += key + "=" + Convert.ToString(value) + "&";
        }
        protected static string finish()
        {
            if (api_str.Length > 0)
            {
                api_str = api_str.Remove(api_str.Length - 1);
            }
            return api_str;
        }
        protected static string datetimetostring(DateTime dt)
        {
            return (dt.ToString("YYYY-M-DThh:mm:ss"));
        }
    }
    class Timeline: BaseAPICall
    {
        public static string getPublicPlurks(string user_id, DateTime offset = new DateTime(), int limit = 100, bool favorers_detail = false, bool limited_detail = false, bool replurkers_detail = false)
        {
            call("Timeline",        "getPublicPlurks");
            par("user_id",          user_id);
            par("offset",           offset);
            par("limit",            limit);
            par("favorers_detail",  favorers_detail);
            par("limited_detail",   limited_detail);
            par("replurkers_detail",replurkers_detail);
            return (finish());
        }
    }
    class Profile : BaseAPICall
    {
        public static string getOwnProfile()
        {
            call("Profile", "getOwnProfile");
            return (finish());
        }
        public static string getPublicProfile(string user_id)
        {
            call("Profile", "getPublicProfile");
            par("user_id",  user_id);
            return (finish());
        }
    }
    class PlurkSearch : BaseAPICall
    {
        public static string search(string query, int offset = -1)
        {
            call("PlurkSearch", "search");
            par("query",        query);
            par("offset",       offset,     Check.IntCheckEqualOrGreaterThanZero);
            return (finish());
        }
    }
    class FriendsFans : BaseAPICall
    {
        public static string getFriendsByOffset(string user_id, int offset = -1, int limit = -1)
        {
            call("FriendsFans", "getFriendsByOffset");
            par("user_id",      user_id);
            par("offset",       offset,     Check.IntCheckEqualOrGreaterThanZero);
            par("limit",        limit,      Check.IntCheckEqualOrGreaterThanZero);
            return (finish());
        }
        public static string getFansByOffset(string user_id, int offset = -1, int limit = -1)
        {
            call("FriendsFans", "getFansByOffset");
            par("user_id",      user_id);
            par("offset",       offset,     Check.IntCheckEqualOrGreaterThanZero);
            par("limit",        limit,      Check.IntCheckEqualOrGreaterThanZero);
            return (finish());
        }
    }
    class Result
    {
        private static Dictionary<string, object>[] array_result(object _api_result, string _key = null)
        {
            if (_api_result == null)
            {
                return (null);
            }
            try
            {
                object[] _objects = null;
                if (_api_result.GetType().IsArray && (_key == null))
                {
                    _objects = (object[])_api_result;
                }
                else
                {
                    var _dict = (Dictionary<string, object>)_api_result;
                    if (_dict.ContainsKey(_key))
                    {
                        _objects = (object[])_dict[_key];
                    }
                }
                if (_objects != null)
                {
                    var _out = new Dictionary<string, object>[_objects.Length];
                    int _idx = 0;
                    foreach (Dictionary<string, object> _fields in _objects)
                    {
                        _out[_idx++] = _fields;
                    }
                    return (_out);
                }
            }
            catch (InvalidCastException)
            {
            }
            return (null);
        }
        private static Dictionary<string, object>[] multi_dict_result(object _api_result, string _key)
        {
            if (_api_result == null)
            {
                return (null);
            }
            try
            {
                var _dict = (Dictionary<string, object>)_api_result;
                if (_dict.ContainsKey(_key))
                {
                    var _users = (Dictionary<string, object>)_dict[_key];
                    var _out = new Dictionary<string, object>[_users.Count];
                    int _idx = 0;
                    foreach (Dictionary<string, object> _fields in _users.Values)
                    {
                        _out[_idx++] = _fields;
                    }
                    return _out;
                }
            }
            catch (InvalidCastException)
            {
            }
            return (null);
        }
        private static Dictionary<string, object> single_dict_result(object _api_result, string _key)
        {
            if (_api_result == null)
            {
                return (null);
            }
            try
            {
                Dictionary<string, object> _dict = (Dictionary<string, object>)_api_result;
                if (_dict.ContainsKey(_key))
                {
                    return (Dictionary<string, object>)_dict[_key];
                }
            }
            catch (InvalidCastException)
            {
            }
            return (null);
        }
        public static Dictionary<string, object>[] plurks(object _api_result)
        {
            return array_result(_api_result, "plurks");
        }
        public static Dictionary<string, object>[] friends(object _api_result)
        {
            return array_result(_api_result);
        }
        public static Dictionary<string, object>[] fans(object _api_result)
        {
            return array_result(_api_result);
        }
        public static Dictionary<string, object>[] plurk_users(object _api_result)
        {
            return multi_dict_result(_api_result, "plurk_users");
        }
        public static Dictionary<string, object>[] users(object _api_result)
        {
            return multi_dict_result(_api_result, "users");
        }
        public static Dictionary<string, object> user(object _api_result)
        {
            return single_dict_result(_api_result, "user");
        }
        public static Dictionary<string, object> user_info(object _api_result)
        {
            return single_dict_result(_api_result, "user_info");
        }
    }
    class Plorx
    {
        private OAuthBase oauth = new OAuthBase();
        private string oauth_token = null, oauth_token_secret = null, oauth_verifier = null;
        private string oauth_consumer_key = "OkVGdT5JHGJt", oauth_consumer_secret = "ThVTB7Jby0gd1LTcJh9orFJk6ylTZTxV";
        private CookieContainer cookies = new CookieContainer();
        private const string host = "http://www.plurk.com";
        private const string host_safe = "https://www.plurk.com";
        private object api_result = null;
        private Dictionary<string, object> json_dict = new Dictionary<string, object>();

        public Plorx(string _login = "plorx", string _password = "RGWrMZMw9Hyo")
        {
            try
            {
                Request(host_safe + "/Users/login?nick_name=" + _login + "&password=" + _password + "&login_token=" + oauth.GenerateNonce() + "&logintoken=1");
                string response = Request(CreateOAuthSignature(host + "/OAuth/request_token"));
                oauth_token = GetQueryParameter(response, "oauth_token");
                oauth_token_secret = GetQueryParameter(response, "oauth_token_secret");
                using (WebBrowser browser = new WebBrowser())
                {
                    browser.ScriptErrorsSuppressed = true;
                    browser.Navigate("about:blank");
                    browser.Document.Write(Request(host + "/OAuth/authorizeDone?oauth_token=" + oauth_token + "&accept=1"));
                    oauth_verifier = browser.Document.GetElementById("oauth_verifier").InnerText;
                }
                response = Request(CreateOAuthSignature(host + "/OAuth/access_token", false));
                oauth_token = GetQueryParameter(response, "oauth_token");
                oauth_token_secret = GetQueryParameter(response, "oauth_token_secret");
            }
            catch (WebException e)
            {
                Console.WriteLine("Error: " + e.Message);
            }
        }

        public object MakeAPICall(string _request)
        {
            api_result = null;
            try
            {
                api_result = (new JavaScriptSerializer()).DeserializeObject(Request(CreateOAuthSignature(host + _request)));
            }
            catch (WebException e)
            {
                Console.WriteLine("Error: " + e.Message);
            }
            return (api_result);
        }

        public object APIResult()
        {
            return (api_result);            
        }

        public Dictionary<string, object> APIResult(string _key)
        {
            json_dict.Clear();
            ParseJSON(api_result);
            Dictionary<string, object> _json_dict = new Dictionary<string, object>();
            foreach (KeyValuePair<string, object> _pair in json_dict)
            {
                if (_pair.Key.StartsWith(_key))
                {
                    _json_dict[_pair.Key] = _pair.Value;
                }
            }
            return (_json_dict);
        }

        public string PrintAPIResult()
        {
            json_dict.Clear();
            ParseJSON(api_result);
            string _log = "";
            foreach (KeyValuePair<string, object> _pair in json_dict)
            {
                _log += _pair.Key + "=" + _pair.Value.ToString() + "\r\n";
            }
            return (_log);
        }

        public delegate void FriendsEventHandler(Dictionary<string, object>[] _friends, int _total);
        public event FriendsEventHandler FriendsEvent;
        public Dictionary<string, object>[] GetAllFriends(string _user_id)
        {
            _user_id = user_id(_user_id);            
            int _offset = 0;
            int _limit = 30;
            var _list = new List<Dictionary<string, object>>();
            do
            {
                MakeAPICall(FriendsFans.getFriendsByOffset(_user_id, _offset, _limit));
                if (friends == null)
                {
                    return new Dictionary<string, object>[0];
                }
                _list.AddRange(friends);
                FriendsEvent(friends, _list.Count);
                _offset += _limit;
            }
            while (friends.Length > 0);
            api_result = _list.ToArray();
            return ((Dictionary<string, object>[])api_result);
        }

        public delegate void FansEventHandler(Dictionary<string, object>[] _fans, int _count);
        public event FansEventHandler FansEvent;
        public Dictionary<string, object>[] GetAllFans(string _user_id)
        {
            _user_id = user_id(_user_id);
            int _offset = 0;
            int _limit = 30;
            var _list = new List<Dictionary<string, object>>();
            do
            {
                MakeAPICall(FriendsFans.getFansByOffset(_user_id, _offset, _limit));
                if (fans == null)
                {
                    return new Dictionary<string, object>[0];
                }
                _list.AddRange(fans);
                FansEvent(fans, _list.Count);
                _offset += _limit;
            }
            while (fans.Length > 0);
            api_result = _list.ToArray();
            return ((Dictionary<string, object>[])api_result);
        }

        public string user_id(string _user_name_or_id)
        {
            try
            {
                long _user_id = Convert.ToInt64(_user_name_or_id);
                return (_user_name_or_id);
            }
            catch
            {
            }
            MakeAPICall(Profile.getPublicProfile(_user_name_or_id));
            if (user_info == null)
            {
                return null;
            }
            return (user_info["uid"].ToString());
        }

        public Dictionary<string, object>[] plurks
        {
            get { return Result.plurks(APIResult()); }
        }
        public Dictionary<string, object>[] friends
        {
            get { return Result.friends(APIResult()); }
        }
        public Dictionary<string, object>[] fans
        {
            get { return Result.fans(APIResult()); }
        }
        public Dictionary<string, object>[] plurk_users
        {
            get { return Result.plurk_users(APIResult()); }
        }
        public Dictionary<string, object>[] users
        {
            get { return Result.users(APIResult()); }
        }
        public Dictionary<string, object> user
        {
            get { return Result.user(APIResult()); }
        }
        public Dictionary<string, object> user_info
        {
            get { return Result.user_info(APIResult()); }
        }

        public static string ListToString(List<string> _list, string _separator = ".")
        {
            if (_list == null)
            {
                return "";
            }
            if (_list.Count == 0)
            {
                return "root";
            }
            string _str = "";
            foreach (string _p in _list)
            {
                _str += _p + _separator;
            }
            return (_str.Remove(_str.Length - _separator.Length));
        }
        private object ParseJSON(object _src_obj, List<string> _src_path = null)
        {
            if (_src_obj == null)
            {
                return "";
            }
            if (_src_path == null)
            {
                _src_path = new List<string>();
            }
            Dictionary<string, object> _d;
            if (_src_obj.GetType().IsInstanceOfType(new Dictionary<string, object>()))
            {
                _d = (Dictionary<string, object>)_src_obj;
                json_dict[ListToString(_src_path) + ".count"] = _d.Count;
            }
            else if (_src_obj.GetType().IsArray)
            {
                _d = new Dictionary<string, object>();
                int _idx = 0;
                foreach (object _e in (object[])_src_obj)
                {
                    _d[Convert.ToString(_idx++)] = _e;
                }
                json_dict[ListToString(_src_path) + ".count"] = _d.Count;
            }
            else
            {
                return _src_obj;
            }
            foreach (string _key in _d.Keys)
            {
                List<string> _dst_path = new List<string>(_src_path);
                _dst_path.Add(_key);
                object _result = ParseJSON(_d[_key], _dst_path);
                if (_result != null)
                {
                    json_dict[ListToString(_dst_path)] = _result;
                }
            }
            return null;
        }
        private string Request(string uri, string method = "GET")
        {
            Console.WriteLine(method + ": " + uri);
            WebRequest request = WebRequest.Create(uri);
            ((HttpWebRequest)request).CookieContainer = cookies;
            request.Method = method;
            Application.DoEvents();
            return (new StreamReader(request.GetResponse().GetResponseStream())).ReadToEnd();
        }
        private string CreateOAuthSignature(string base_address, bool no_verifier = true)
        {
            string normalizedUrl, normalizedRequest;
            string oauth_signature = oauth.GenerateSignature(
                new Uri(base_address),
                null,
                oauth_consumer_key,
                oauth_consumer_secret,
                oauth_token,
                oauth_token_secret,
                "GET",
                oauth.GenerateTimeStamp(),
                no_verifier ? null : oauth_verifier,
                oauth.GenerateNonce(),
                out normalizedUrl,
                out normalizedRequest);
            return (normalizedUrl + "?" + normalizedRequest + "&oauth_signature=" + OAuthBase.UrlEncode(oauth_signature));
        }
        private string GetQueryParameter(string parameters, string key)
        {
            foreach (string p in parameters.Split('&'))
            {
                string[] s = p.Split('=');
                if (s.Length == 2)
                {
                    if (s[0] == key)
                    {
                        return s[1];
                    }
                }
            }
            return null;
        }
    }
}
